Correctly upload textures for GLES
authorAlexander Larsson <alexl@redhat.com>
Thu, 24 Sep 2020 13:03:48 +0000 (15:03 +0200)
committerAlexander Larsson <alexl@redhat.com>
Fri, 25 Sep 2020 07:31:43 +0000 (09:31 +0200)
GLES doesn't support the GL_BGRA +  GL_UNSIGNED_INT_24_8 hack that
we use on desktop OpenGL to upload textures directly in the cairo
pixel format. This adds the required conversions to all the places
that currently need it.

We also add a data_format to the internal gdk_gl_context_upload_texture()
function to make it clearer what the format are. Currently it is always
the cairo image surface format, but eventually we want to support other
formats so that we can avoid some of the unnecessary conversions we do.

Also, the current gdk_gl_context_upload_texture() code always converts
to a cairo format and uploads that like we did before. Later commits
will allow this to use other upload formats that gl supports to avoid
conversions.

gdk/gdkgl.c
gdk/gdkglcontext.c
gdk/gdkglcontextprivate.h
gsk/gl/gskglglyphcache.c
gsk/gl/gskgliconcache.c

index 309f792613acf3123d0e8504a3ed49fca5c5d220..2fe5cc863ed1305f1506d0a03482606ff9cd59f3 100644 (file)
@@ -482,6 +482,7 @@ gdk_cairo_surface_upload_to_gl (cairo_surface_t *surface,
                                  rect.width,
                                  rect.height,
                                  cairo_image_surface_get_stride (tmp),
+                                 GDK_MEMORY_DEFAULT,
                                  target);
 
   cairo_surface_unmap_image (surface, tmp);
index 2843ef05f5589f04425c11bba34e0ed9939bd0b7..9d658660e2041d71c28f0834376ac8e89d199b9a 100644 (file)
@@ -87,6 +87,7 @@
 
 #include "gdkglcontextprivate.h"
 #include "gdkdisplayprivate.h"
+#include "gdkmemorytextureprivate.h"
 #include "gdkinternals.h"
 
 #include "gdkintl.h"
@@ -227,49 +228,81 @@ gdk_gl_context_upload_texture (GdkGLContext    *context,
                                int              width,
                                int              height,
                                int              stride,
+                               GdkMemoryFormat  data_format,
                                guint            texture_target)
 {
   GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
+  guchar *copy = NULL;
+  guint gl_format;
+  guint gl_type;
 
   g_return_if_fail (GDK_IS_GL_CONTEXT (context));
 
+  if (priv->use_es)
+    {
+      /* GLES only supports rgba, so convert if necessary */
+      if (data_format != GDK_MEMORY_R8G8B8A8_PREMULTIPLIED)
+        {
+          copy = g_malloc (width * height * 4);
+          gdk_memory_convert (copy, width * 4,
+                              GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
+                              data, stride, data_format,
+                              width, height);
+          stride = width * 4;
+          data = copy;
+        }
+
+      gl_format = GL_RGBA;
+      gl_type = GL_UNSIGNED_BYTE;
+    }
+  else
+    {
+      if (data_format == GDK_MEMORY_DEFAULT) /* Cairo surface format */
+        {
+          gl_format = GL_BGRA;
+          gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
+        }
+      else /* Fall-back, convert to cairo-surface-format */
+        {
+          copy = g_malloc (width * height * 4);
+          gdk_memory_convert (copy, width * 4,
+                              GDK_MEMORY_DEFAULT,
+                              data, stride, data_format,
+                              width, height);
+          stride = width * 4;
+          data = copy;
+          gl_format = GL_BGRA;
+          gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
+        }
+    }
+
+
   /* GL_UNPACK_ROW_LENGTH is available on desktop GL, OpenGL ES >= 3.0, or if
    * the GL_EXT_unpack_subimage extension for OpenGL ES 2.0 is available
    */
-  if (!priv->use_es ||
-      (priv->use_es && (priv->gl_version >= 30 || priv->has_unpack_subimage)))
+  if (stride == width * 4)
+    {
+      glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, data);
+    }
+  else if (!priv->use_es ||
+           (priv->use_es && (priv->gl_version >= 30 || priv->has_unpack_subimage)))
     {
       glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
       glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / 4);
 
-      if (priv->use_es)
-        glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
-                      data);
-      else
-        glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
-                      data);
+      glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, data);
 
       glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
     }
   else
     {
       int i;
-
-      if (priv->use_es)
-        {
-          glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-
-          for (i = 0; i < height; i++)
-            glTexSubImage2D (texture_target, 0, 0, i, width, 1, GL_RGBA, GL_UNSIGNED_BYTE, data + (i * stride));
-        }
-      else
-        {
-          glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
-
-          for (i = 0; i < height; i++)
-            glTexSubImage2D (texture_target, 0, 0, i, width, 1, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data + (i * stride));
-        }
+      glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, NULL);
+      for (i = 0; i < height; i++)
+        glTexSubImage2D (texture_target, 0, 0, i, width, 1, gl_format, gl_type, data + (i * stride));
     }
+
+  g_free (copy);
 }
 
 static gboolean
index 8991332b561bf03c0098e707f3f4b7a9609eb760..e785fbc63d66e3e119cb5175548e5f381a37ed37 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "gdkglcontext.h"
 #include "gdkdrawcontextprivate.h"
+#include "gdkmemorytexture.h"
 
 G_BEGIN_DECLS
 
@@ -84,6 +85,7 @@ void                    gdk_gl_context_upload_texture           (GdkGLContext
                                                                  int              width,
                                                                  int              height,
                                                                  int              stride,
+                                                                 GdkMemoryFormat  data_format,
                                                                  guint            texture_target);
 GdkGLContextPaintData * gdk_gl_context_get_paint_data           (GdkGLContext    *context);
 gboolean                gdk_gl_context_use_texture_rectangle    (GdkGLContext    *context);
index 4d23eb60be93cf630b13ac1596b03018268e0b71..b147dcef216593e4ea98bc99fac9dc9bb3770962 100644 (file)
@@ -7,6 +7,7 @@
 #include "gskgltextureatlasprivate.h"
 
 #include "gdk/gdkglcontextprivate.h"
+#include "gdk/gdkmemorytextureprivate.h"
 
 #include <graphene.h>
 #include <cairo.h>
@@ -186,6 +187,10 @@ upload_glyph (GlyphCacheKey    *key,
               GskGLCachedGlyph *value)
 {
   GskImageRegion r;
+  guchar *pixel_data;
+  guchar *free_data = NULL;
+  guint gl_format;
+  guint gl_type;
 
   gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
                                           "Uploading glyph %d",
@@ -197,15 +202,27 @@ upload_glyph (GlyphCacheKey    *key,
       glBindTexture (GL_TEXTURE_2D, value->texture_id);
 
       if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
-        glTexSubImage2D (GL_TEXTURE_2D, 0, r.x, r.y, r.width, r.height,
-                         GL_RGBA, GL_UNSIGNED_BYTE,
-                         r.data);
+        {
+          pixel_data = free_data = g_malloc (r.width * r.height * 4);
+          gdk_memory_convert (pixel_data, r.width * 4,
+                              GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
+                              r.data, r.width * 4,
+                              GDK_MEMORY_DEFAULT, r.width, r.height);
+          gl_format = GL_RGBA;
+          gl_type = GL_UNSIGNED_BYTE;
+        }
       else
-        glTexSubImage2D (GL_TEXTURE_2D, 0, r.x, r.y, r.width, r.height,
-                         GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
-                         r.data);
+        {
+          pixel_data = r.data;
+          gl_format = GL_BGRA;
+          gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
+        }
+
+      glTexSubImage2D (GL_TEXTURE_2D, 0, r.x, r.y, r.width, r.height,
+                       gl_format, gl_type, pixel_data);
       glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
       g_free (r.data);
+      g_free (free_data);
     }
 
   gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
index 5097ae597eb0b83b66b79e1dfe4c390bb4629cb2..4bbbdbb4da12a29ba537ce9171f7ed652e5390f8 100644 (file)
@@ -1,6 +1,7 @@
 #include "gskgliconcacheprivate.h"
 #include "gskgltextureatlasprivate.h"
 #include "gdk/gdktextureprivate.h"
+#include "gdk/gdkmemorytextureprivate.h"
 #include "gdk/gdkglcontextprivate.h"
 
 #include <epoxy/gl.h>
@@ -134,7 +135,10 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache  *self,
     int packed_y = 0;
     cairo_surface_t *surface;
     unsigned char *surface_data;
+    unsigned char *pixel_data;
+    guchar *free_data = NULL;
     guint gl_format;
+    guint gl_type;
 
     gsk_gl_texture_atlases_pack (self->atlases, width + 2, height + 2, &atlas, &packed_x, &packed_y);
 
@@ -158,36 +162,47 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache  *self,
                                             "Uploading texture");
 
     if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
-      gl_format = GL_RGBA;
+      {
+        pixel_data = free_data = g_malloc (width * height * 4);
+        gdk_memory_convert (pixel_data, width * 4,
+                            GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
+                            surface_data, cairo_image_surface_get_stride (surface),
+                            GDK_MEMORY_DEFAULT, width, height);
+        gl_format = GL_RGBA;
+        gl_type = GL_UNSIGNED_BYTE;
+      }
     else
-      gl_format = GL_BGRA;
+      {
+        pixel_data = surface_data;
+        gl_format = GL_BGRA;
+        gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
+      }
 
     glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
 
     glTexSubImage2D (GL_TEXTURE_2D, 0,
                      packed_x + 1, packed_y + 1,
                      width, height,
-                     gl_format,
-                     GL_UNSIGNED_BYTE,
-                     surface_data);
+                     gl_format, gl_type,
+                     pixel_data);
     /* Padding top */
     glTexSubImage2D (GL_TEXTURE_2D, 0,
                      packed_x + 1, packed_y,
                      width, 1,
-                     gl_format, GL_UNSIGNED_BYTE,
-                     surface_data);
+                     gl_format, gl_type,
+                     pixel_data);
     /* Padding left */
     glTexSubImage2D (GL_TEXTURE_2D, 0,
                      packed_x, packed_y + 1,
                      1, height,
-                     gl_format, GL_UNSIGNED_BYTE,
-                     surface_data);
+                     gl_format, gl_type,
+                     pixel_data);
     /* Padding top left */
     glTexSubImage2D (GL_TEXTURE_2D, 0,
                      packed_x, packed_y,
                      1, 1,
-                     gl_format, GL_UNSIGNED_BYTE,
-                     surface_data);
+                     gl_format, gl_type,
+                     pixel_data);
 
     /* Padding right */
     glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
@@ -195,14 +210,14 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache  *self,
     glTexSubImage2D (GL_TEXTURE_2D, 0,
                      packed_x + width + 1, packed_y + 1,
                      1, height,
-                     gl_format, GL_UNSIGNED_BYTE,
-                     surface_data);
+                     gl_format, gl_type,
+                     pixel_data);
     /* Padding top right */
     glTexSubImage2D (GL_TEXTURE_2D, 0,
                      packed_x + width + 1, packed_y,
                      1, 1,
-                     gl_format, GL_UNSIGNED_BYTE,
-                     surface_data);
+                     gl_format, gl_type,
+                     pixel_data);
     /* Padding bottom */
     glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
     glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
@@ -210,22 +225,22 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache  *self,
     glTexSubImage2D (GL_TEXTURE_2D, 0,
                      packed_x + 1, packed_y + 1 + height,
                      width, 1,
-                     gl_format, GL_UNSIGNED_BYTE,
-                     surface_data);
+                     gl_format, gl_type,
+                     pixel_data);
     /* Padding bottom left */
     glTexSubImage2D (GL_TEXTURE_2D, 0,
                      packed_x, packed_y + 1 + height,
                      1, 1,
-                     gl_format, GL_UNSIGNED_BYTE,
-                     surface_data);
+                     gl_format, gl_type,
+                     pixel_data);
     /* Padding bottom right */
     glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
     glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1);
     glTexSubImage2D (GL_TEXTURE_2D, 0,
                      packed_x + 1 + width, packed_y + 1 + height,
                      1, 1,
-                     gl_format, GL_UNSIGNED_BYTE,
-                     surface_data);
+                     gl_format, gl_type,
+                     pixel_data);
     /* Reset this */
     glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
     glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
@@ -236,6 +251,7 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache  *self,
     *out_icon_data = icon_data;
 
     cairo_surface_destroy (surface);
+    g_free (free_data);
 
 #if 0
     {